<?php
// +----------------------------------------------------------------------
// | QSCMS
// +----------------------------------------------------------------------
// | Copyright (c)  2025 https://www.qqss.net All rights reserved.
// +----------------------------------------------------------------------
// | Licensed QSCMS is not free software, commercial use must purchase official license from official website to avoid unnecessary legal disputes.
// +----------------------------------------------------------------------
// | Author: Contract 990504246@qq.com
// +----------------------------------------------------------------------

namespace app\common\plugin;

/**
 * 应用管理器
 */
class PluginManager
{
    private $loaded_plugins = [];

    public function __construct()
    {
        // 私有构造函数，实现单例模式
    }

    /**
     * 卸载应用
     */
    public function uninstallPlugin($plugin_name)
    {
        // 检查应用是否开启
        if (is_plugin_enabled($plugin_name)) {
            throw new \Exception('请先关闭应用后再卸载');
        }

        // 清理插件实例缓存
        if (isset($this->loaded_plugins[$plugin_name])) {
            unset($this->loaded_plugins[$plugin_name]);
        }

        // 自动删除插件创建的数据库表
        $this->dropPluginTables($plugin_name);

        // 直接删除应用目录
        $plugin_path = get_plugin_path($plugin_name);
        if (is_dir($plugin_path)) {
            $this->deleteDirectory($plugin_path);
        }

        return true;
    }

    /**
     * 启用应用
     */
    public function enablePlugin($plugin_name)
    {
        // 调用插应用的额外启用办法
        $plugin_instance = $this->getPluginInstance($plugin_name);
        if ($plugin_instance && method_exists($plugin_instance, 'activate')) {
            $plugin_instance->activate();
        }

        // 系统默认启用办法
        $plugin_path = get_plugin_path($plugin_name);
        $info_file   = $plugin_path . '/info.ini';

        if (!file_exists($info_file)) {
            return false;
        }

        $info = parse_ini_file($info_file);
        if (!$info) {
            return false;
        }

        // 更新 info.ini 中的 status
        $info['status'] = 1;
        $ini_content    = '';
        foreach ($info as $key => $value) {
            $ini_content .= "$key = $value\n";
        }

        file_put_contents($info_file, $ini_content);

        // 清除钩子缓存
        \app\common\plugin\HookRegistry::reload();

        // 重载 webman
        reload_webman();

        return true;
    }

    /**
     * 禁用插应用
     */
    public function disablePlugin($plugin_name)
    {
        // 调用应用的额外停用方法
        $plugin_instance = $this->getPluginInstance($plugin_name);
        if ($plugin_instance && method_exists($plugin_instance, 'deactivate')) {
            $plugin_instance->deactivate();
        }

        // 清理插件实例缓存，避免内存泄露
        if (isset($this->loaded_plugins[$plugin_name])) {
            unset($this->loaded_plugins[$plugin_name]);
        }
        $plugin_path = get_plugin_path($plugin_name);
        $info_file   = $plugin_path . '/info.ini';

        if (!file_exists($info_file)) {
            return false;
        }

        $info = parse_ini_file($info_file);
        if (!$info) {
            return false;
        }

        // 更新 info.ini 中的 status
        $info['status'] = 0;
        $ini_content    = '';
        foreach ($info as $key => $value) {
            $ini_content .= "$key = $value\n";
        }

        file_put_contents($info_file, $ini_content);

        // 清除钩子缓存
        \app\common\plugin\HookRegistry::reload();

        // 重载 webman
        reload_webman();
        return true;
    }

    /**
     * 获取应用实例
     */
    public function getPluginInstance($plugin_name)
    {
        if (isset($this->loaded_plugins[$plugin_name])) {
            return $this->loaded_plugins[$plugin_name];
        }

        $plugin_path = get_plugin_path($plugin_name);
        $class_file  = $plugin_path . '/Plugin.php';

        if (file_exists($class_file)) {
            require_once $class_file;

            $class_name = "\\plugins\\{$plugin_name}\\Plugin";
            if (class_exists($class_name)) {
                $this->loaded_plugins[$plugin_name] = new $class_name($plugin_name);
                return $this->loaded_plugins[$plugin_name];
            }
        }

        return null;
    }

    /**
     * 清理所有插件实例缓存，避免内存泄露
     */
    public function clearPluginCache()
    {
        $count                = count($this->loaded_plugins);
        $this->loaded_plugins = [];

        // 强制垃圾回收
        if (function_exists('gc_collect_cycles')) {
            gc_collect_cycles();
        }

        return $count;
    }

    /**
     * 获取当前缓存的插件数量
     */
    public function getCachedPluginCount()
    {
        return count($this->loaded_plugins);
    }

    /**
     * 获取已安装的应用列表（我的应用）
     */
    public function getInstalledPlugins()
    {
        $plugins     = [];
        $plugins_dir = base_path() . '/plugins/';

        if (!is_dir($plugins_dir)) {
            return $plugins;
        }

        $dirs = scandir($plugins_dir);
        foreach ($dirs as $dir) {
            if ($dir === '.' || $dir === '..') {
                continue;
            }

            $plugin_path = $plugins_dir . '/' . $dir;
            if (is_dir($plugin_path)) {
                $info_file = $plugin_path . '/info.ini';
                if (file_exists($info_file)) {
                    $plugin_info = parse_ini_file($info_file);
                    if ($plugin_info) {
                        $plugin_info['name']    = $dir;
                        $plugin_info['enabled'] = $this->isPluginEnabled($dir);
                        $plugin_info['path']    = $plugin_path;
                        $plugins[$dir]          = $plugin_info;
                    }
                }
            }
        }

        return $plugins;
    }

    /**
     * 获取应用配置
     */
    public function getPluginConfig($plugin_name)
    {
        $plugin_path = get_plugin_path($plugin_name);
        $config_file = $plugin_path . '/config.php';

        if (file_exists($config_file)) {
            return require $config_file;
        }

        return [];
    }

    /**
     * 保存应用配置
     */
    public function savePluginConfig($plugin_name, $config_data)
    {
        $plugin_path = get_plugin_path($plugin_name);
        $config_file = $plugin_path . '/config.php';

        if (!file_exists($config_file)) {
            return false;
        }

        // 读取原配置结构
        $config_structure = require $config_file;

        if (!is_array($config_structure)) {
            return false;
        }

        // 通过 info.ini 中的 entry 字段判断插件类型
        $plugin_info = $this->getPluginInfo($plugin_name);
        $has_entry   = !empty($plugin_info['entry']);

        if ($has_entry) {
            // 有 entry 的插件：使用简单键值对格式，直接更新值
            foreach ($config_data as $key => $value) {
                $config_structure[$key] = $value;
            }
        } else {
            // 无 entry 的插件：使用标准结构格式
            $is_structured_config = $this->isStructuredConfig($config_structure);

            if ($is_structured_config) {
                // 标准结构格式：更新配置值，保持原结构
                foreach ($config_structure as &$field) {
                    if (isset($config_data[$field['name']])) {
                        $field['value'] = $config_data[$field['name']];
                    }
                }
            } else {
                // 如果不是结构化配置，创建标准结构
                $new_structure = [];
                foreach ($config_data as $key => $value) {
                    $new_structure[] = [
                        'name'        => $key,
                        'value'       => $value,
                        'title'       => ucfirst(str_replace('_', ' ', $key)),
                        'type'        => 'text',
                        'description' => ''
                    ];
                }
                $config_structure = $new_structure;
            }
        }

        // 生成新的配置文件内容，使用现代PHP语法
        $config_content = "<?php\n\n// {$plugin_name} 插件配置\nreturn " . $this->formatArrayAsPhp($config_structure) . ";\n";

        return file_put_contents($config_file, $config_content) !== false;
    }

    /**
     * 检查配置是否为结构化格式
     */
    private function isStructuredConfig($config)
    {
        if (empty($config) || !is_array($config)) {
            return false;
        }

        // 检查第一个元素是否包含标准结构字段
        $first_item = reset($config);
        return is_array($first_item) &&
            isset($first_item['name']) &&
            isset($first_item['value']);
    }


    /**
     * 获取应用信息（从 info.ini 文件读取）
     */
    public function getPluginInfo($plugin_name)
    {
        $plugin_path = get_plugin_path($plugin_name);
        $info_file   = $plugin_path . '/info.ini';

        if (file_exists($info_file)) {
            $info = parse_ini_file($info_file);
            if ($info) {
                return $info;
            }
        }

        return null;
    }

    /**
     * 检查应用是否已安装
     */
    public function isPluginInstalled($plugin_name)
    {
        $plugin_path = get_plugin_path($plugin_name);
        return is_dir($plugin_path) && file_exists($plugin_path . '/info.ini');
    }

    /**
     * 递归删除目录
     */
    private function deleteDirectory($dir)
    {
        if (!is_dir($dir)) {
            return false;
        }

        $files = array_diff(scandir($dir), ['.', '..']);
        foreach ($files as $file) {
            $path = $dir . '/' . $file;
            is_dir($path) ? $this->deleteDirectory($path) : unlink($path);
        }

        return rmdir($dir);
    }

    /**
     * 检查应用是否启用
     */
    private function isPluginEnabled($plugin_name)
    {
        // 首先检查info.ini中的status字段
        $plugin_path = get_plugin_path($plugin_name);
        $info_file   = $plugin_path . '/info.ini';

        if (file_exists($info_file)) {
            $info = parse_ini_file($info_file);
            if (isset($info['status'])) {
                return $info['status'] === '1' || $info['status'] === 1;
            }
        }
        return false;
    }

    /**
     * 删除插件创建的数据库表
     * 通过解析插件的SQL文件，自动删除插件创建的表
     */
    private function dropPluginTables($plugin_name)
    {
        $plugin_path = get_plugin_path($plugin_name);
        $sql_file    = $plugin_path . '/install.sql';

        if (!file_exists($sql_file)) {
            return;
        }

        $tables = $this->extractTablesFromSql($sql_file);
        if (!empty($tables)) {
            $this->dropTables($tables, $plugin_name);
        }
    }

    /**
     * 从SQL文件中提取表名
     * 使用正则表达式匹配 CREATE TABLE 语句
     */
    private function extractTablesFromSql($sql_file)
    {
        $sql_content = file_get_contents($sql_file);
        if (!$sql_content) {
            return [];
        }

        // 使用正则表达式匹配 CREATE TABLE 语句
        // 支持：CREATE TABLE、CREATE TABLE IF NOT EXISTS
        $pattern = '/^CREATE\s+TABLE\s+(IF\s+NOT\s+EXISTS\s+)?`?([a-zA-Z_][a-zA-Z0-9_]*)`?/mi';

        if (!preg_match_all($pattern, $sql_content, $matches)) {
            return [];
        }

        // $matches[2] 包含所有匹配的表名
        $tables = [];
        foreach ($matches[2] as $table_name) {
            $tables[] = trim($table_name, '`');
        }

        return $tables;
    }

    /**
     * 执行删除表的操作
     */
    private function dropTables($tables, $plugin_name)
    {
        foreach ($tables as $table_name) {
            // 检查表是否存在 - 使用 INFORMATION_SCHEMA 查询
            $exists = \think\facade\Db::query(
                "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ?",
                [$table_name]
            );

            if (!empty($exists)) {
                // 删除表
                \think\facade\Db::execute("DROP TABLE IF EXISTS `{$table_name}`");
                // error_log("插件 {$plugin_name} 卸载：成功删除表 {$table_name}");
            }
        }
    }

    /**
     * 获取插件的钩子配置
     */
    public function getPluginHooks($plugin_name)
    {
        $plugin_path = get_plugin_path($plugin_name);
        $events_file = $plugin_path . '/events.php';

        if (!file_exists($events_file)) {
            return [];
        }

        $events = include $events_file;
        if (!is_array($events)) {
            return [];
        }

        // 获取禁用的钩子列表
        $disabled_hooks_file = $plugin_path . '/disabled_hooks.php';
        $disabled_hooks      = [];
        if (file_exists($disabled_hooks_file)) {
            $disabled_hooks = include $disabled_hooks_file;
            if (!is_array($disabled_hooks)) {
                $disabled_hooks = [];
            }
        }

        // 格式化钩子信息
        $hooks = [];
        foreach ($events as $hook_name => $handlers) {
            $hook_info = [
                'hook_name' => $hook_name,
                'enabled'   => !in_array($hook_name, $disabled_hooks),
                'handlers'  => []
            ];

            foreach ($handlers as $index => $handler) {
                if (is_array($handler) && count($handler) >= 2) {
                    $hook_info['handlers'][] = [
                        'index'    => $index,
                        'class'    => $handler[0],
                        'method'   => $handler[1],
                        'priority' => $handler[2] ?? 10
                    ];
                }
            }

            $hooks[] = $hook_info;
        }

        return $hooks;
    }

    /**
     * 获取所有插件的钩子概览
     */
    public function getAllPluginHooks()
    {
        $plugins   = $this->getInstalledPlugins();
        $all_hooks = [];

        foreach ($plugins as $plugin_name => $plugin_info) {
            $hooks = $this->getPluginHooks($plugin_name);
            if (!empty($hooks)) {
                $all_hooks[$plugin_name] = [
                    'plugin_info' => $plugin_info,
                    'hooks'       => $hooks,
                    'hook_count'  => count($hooks)
                ];
            }
        }

        return $all_hooks;
    }

    /**
     * 获取系统中所有已注册的钩子
     */
    public function getSystemHooks()
    {
        $registry_hooks = \app\common\plugin\HookRegistry::getAllHooks();
        $system_hooks   = [];

        foreach ($registry_hooks as $hook_name => $priority_groups) {
            $all_handlers   = [];
            $total_handlers = 0;

            // 遍历所有优先级组
            foreach ($priority_groups as $priority => $handlers) {
                foreach ($handlers as $handler) {
                    $all_handlers[] = [
                        'class'    => $handler['class'] ?? 'Unknown',
                        'method'   => $handler['method'] ?? 'Unknown',
                        'plugin'   => $handler['plugin'] ?? $this->extractPluginFromClass($handler['class'] ?? ''),
                        'priority' => $handler['priority'] ?? $priority
                    ];
                    $total_handlers++;
                }
            }

            if ($total_handlers > 0) {
                $system_hooks[] = [
                    'hook_name'     => $hook_name,
                    'handler_count' => $total_handlers,
                    'handlers'      => $all_handlers
                ];
            }
        }

        return $system_hooks;
    }

    /**
     * 从类名中提取插件名称
     */
    private function extractPluginFromClass($class_name)
    {
        if (preg_match('/^plugins\\\\([^\\\\]+)\\\\/', $class_name, $matches)) {
            return $matches[1];
        }
        return 'system';
    }

    /**
     * 将数组格式化为现代PHP语法（使用[]而不是array()）
     */
    private function formatArrayAsPhp($array, $indent = 0)
    {
        if (!is_array($array)) {
            return var_export($array, true);
        }

        $spaces     = str_repeat('    ', $indent);
        $nextSpaces = str_repeat('    ', $indent + 1);

        // 检查是否为索引数组
        $isIndexed = array_keys($array) === range(0, count($array) - 1);

        $result = "[\n";

        foreach ($array as $key => $value) {
            $result .= $nextSpaces;

            // 如果不是索引数组，添加键
            if (!$isIndexed) {
                $result .= var_export($key, true) . ' => ';
            }

            // 递归处理值
            if (is_array($value)) {
                $result .= $this->formatArrayAsPhp($value, $indent + 1);
            } else {
                $result .= var_export($value, true);
            }

            $result .= ",\n";
        }

        $result .= $spaces . ']';

        return $result;
    }

    /**
     * 禁用插件钩子
     */
    public function disablePluginHook($plugin_name, $hook_name)
    {
        $plugin_path = get_plugin_path($plugin_name);
        $events_file = $plugin_path . '/events.php';

        if (!file_exists($events_file)) {
            throw new \Exception('插件事件文件不存在');
        }

        // 创建禁用钩子的配置文件
        $disabled_hooks_file = $plugin_path . '/disabled_hooks.php';
        $disabled_hooks      = [];

        if (file_exists($disabled_hooks_file)) {
            $disabled_hooks = include $disabled_hooks_file;
            if (!is_array($disabled_hooks)) {
                $disabled_hooks = [];
            }
        }

        if (!in_array($hook_name, $disabled_hooks)) {
            $disabled_hooks[] = $hook_name;
        }

        $content = "<?php\n\n// 禁用的钩子列表\nreturn " . $this->formatArrayAsPhp($disabled_hooks) . ";\n";
        file_put_contents($disabled_hooks_file, $content);

        // 重新加载钩子注册表
        \app\common\plugin\HookRegistry::reload();

        // 重载 webman
        reload_webman();

        return true;
    }

    /**
     * 启用插件钩子
     */
    public function enablePluginHook($plugin_name, $hook_name)
    {
        $plugin_path         = get_plugin_path($plugin_name);
        $disabled_hooks_file = $plugin_path . '/disabled_hooks.php';

        if (!file_exists($disabled_hooks_file)) {
            return true; // 没有禁用文件，说明钩子本来就是启用的
        }

        $disabled_hooks = include $disabled_hooks_file;
        if (!is_array($disabled_hooks)) {
            return true;
        }

        $disabled_hooks = array_filter($disabled_hooks, function ($hook) use ($hook_name) {
            return $hook !== $hook_name;
        });

        if (empty($disabled_hooks)) {
            // 如果没有禁用的钩子了，删除文件
            unlink($disabled_hooks_file);
        } else {
            $content = "<?php\n\n// 禁用的钩子列表\nreturn " . $this->formatArrayAsPhp($disabled_hooks) . ";\n";
            file_put_contents($disabled_hooks_file, $content);
        }

        // 重新加载钩子注册表
        \app\common\plugin\HookRegistry::reload();

        // 重载 webman
        reload_webman();

        return true;
    }

    /**
     * 删除插件钩子
     */
    public function deletePluginHook($plugin_name, $hook_name)
    {
        $plugin_path = get_plugin_path($plugin_name);
        $events_file = $plugin_path . '/events.php';

        if (!file_exists($events_file)) {
            throw new \Exception('插件事件文件不存在');
        }

        $events = include $events_file;
        if (!is_array($events)) {
            throw new \Exception('事件文件格式错误');
        }

        if (!isset($events[$hook_name])) {
            throw new \Exception('钩子不存在');
        }

        // 删除钩子
        unset($events[$hook_name]);

        // 重新写入文件
        $content = "<?php\n\n/**\n * " . $plugin_name . " 插件事件配置\n */\n\nreturn " . $this->formatArrayAsPhp($events) . ";\n";
        file_put_contents($events_file, $content);

        // 重新加载钩子注册表
        \app\common\plugin\HookRegistry::reload();

        // 重载 webman
        reload_webman();

        return true;
    }

    /**
     * 更新钩子优先级
     */
    public function updateHookPriority($plugin_name, $hook_name, $handler_index, $priority)
    {
        $plugin_path = get_plugin_path($plugin_name);
        $events_file = $plugin_path . '/events.php';

        if (!file_exists($events_file)) {
            throw new \Exception('插件事件文件不存在');
        }

        $events = include $events_file;
        if (!is_array($events)) {
            throw new \Exception('事件文件格式错误');
        }

        if (!isset($events[$hook_name])) {
            throw new \Exception('钩子不存在');
        }

        if (!isset($events[$hook_name][$handler_index])) {
            throw new \Exception('处理器不存在');
        }

        // 更新优先级
        if (count($events[$hook_name][$handler_index]) >= 3) {
            $events[$hook_name][$handler_index][2] = intval($priority);
        } else {
            $events[$hook_name][$handler_index][] = intval($priority);
        }

        // 重新写入文件
        $content = "<?php\n\n/**\n * " . $plugin_name . " 插件事件配置\n */\n\nreturn " . $this->formatArrayAsPhp($events) . ";\n";
        file_put_contents($events_file, $content);

        // 重新加载钩子注册表
        \app\common\plugin\HookRegistry::reload();

        // 重载 webman
        reload_webman();

        return true;
    }
}
